import random
import socket
import time
from typing import Tuple


def RTxt(p):
  with open(p, encoding='utf8') as f:
    return f.read()


def WTxt(p, t):
  with open(p, 'w', encoding='utf8') as f:
    f.write(t)


class NodeMap:
  def __init__(self):
    self.nodes = []  # 节点列表，存储所有节点信息
    self.links = []  # 连接列表，存储节点之间的连接信息

  def AddNode(self, title, content, pos, color=None):
    """
    添加一个节点。
    :param title: 节点标题
    :param content: 节点内容
    :param pos: 节点位置 (x, y)
    :param color: 颜色 (r, g, b)
    :return: 新增节点的索引
    """
    if color is None:
      color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    self.nodes.append(
      {
        'title': title,
        'content': content,
        'color': color,
        'pos': pos,
      }
    )
    return len(self.nodes) - 1

  def AddLink(self, sourceIndex, targetIndex, content):
    """
    添加节点之间的连接。
    :param sourceIndex: 源节点的索引
    :param targetIndex: 目标节点的索引
    :param content: 连接的内容或描述
    """
    if sourceIndex >= len(self.nodes) or targetIndex >= len(self.nodes):
      raise ValueError("源节点或目标节点索引无效。")
    self.links.append({
      'sourceIndex': sourceIndex,
      'targetIndex': targetIndex,
      'content': content,
    })
    return len(self.links) - 1

  def NodeCount(self):
    """
    获取当前节点的数量。
    :return: 节点总数
    """
    return len(self.nodes)

  def LinkCount(self):
    """
    获取当前连接的数量。
    :return: 连接总数
    """
    return len(self.links)

  def LoadNote(self, note: str):
    """
    从字符串中加载节点和连接信息。
    :param note: 包含节点和连接信息的字符串
    :return: self，支持链式调用
    """
    note = note.replace('\r\n', '\n')
    firstLine = []
    nodesCount = -1
    linksCount = -1
    for line in note.split('\n'):
      if not firstLine:
        firstLine = line.split('\x02')
        nodesCount = int(firstLine[1])
        linksCount = int(firstLine[2])
      else:
        if len(self.nodes) < nodesCount:
          data = line.split('\x02')
          self.nodes.append({
            'title': data[2],
            'content': data[3],
            'pos': (float(data[0]), float(data[1]))
          })
        elif len(self.links) < linksCount:
          data = line.split('\x02')
          self.links.append({
            'sourceIndex': int(data[0]),
            'targetIndex': int(data[1]),
            'content': data[2]
          })
    return self

  def DrawNote(self, api: 'Note2dApi'):
    """
    使用 Note2dApi 绘制节点和连接。
    :param api: Note2dApi 对象，用于绘图
    """
    api.ClearNote()
    for node in self.nodes:
      api.AddNode(
        node['pos'][0],
        node['pos'][1],
        node['title'],
        node['content'],
        node.get('color', (255, 128, 128)),
      )
    for link in self.links:
      api.AddLink(
        link['sourceIndex'],
        link['targetIndex'],
        link['content'],
      )

  def __repr__(self):
    """
    返回类的字符串表示。
    :return: 字符串形式的类内容
    """
    return f'<{self.__class__.__name__} nodes={len(self.nodes)} links={len(self.links)}>'


class Note2dApi:
  def __init__(self, connect=True):
    self.s = socket.socket()
    self.host = socket.gethostname()
    self.port = 20000
    if connect:
      self.s.connect((self.host, self.port))

  @staticmethod
  def FormatText(text):
    if isinstance(text, str):
      return text.replace(' ', r'\_')
    return text

  @staticmethod
  def VBColor(color: Tuple[int, int, int]):
    return color[0] + color[1] * 256 + color[2] * 65536

  def SendData(self, m):
    # print('api:', m)
    msg = f'start extra long command transmission mode:{len(m.encode("gbk"))}'
    self.s.send(msg.encode('gbk'))
    _data = self.s.recv(1024)
    # print('note2d:', data.decode('gbk'))
    self.s.send(m.encode('gbk'))
    _data = self.s.recv(1024)
    # print('note2d:', data.decode('gbk'))

  def SendData2(self, m):
    # 告诉对方将要发送的数据长度
    msg = f'start extra long command transmission mode:{len(m.encode("gbk"))}'
    self.s.send(msg.encode('gbk'))

    # 接收服务器响应
    _response = self.s.recv(1024).decode('gbk')
    # print('Server response to start message:', response)

    # 发送实际数据
    self.s.send(m.encode('gbk'))

    # 准备接收返回的数据
    buffer = b''
    while True:
      chunk = self.s.recv(1024)
      buffer += chunk
      if b'\n\n' in buffer:  # 如果没有数据了，说明已经接收完毕
        break

    # 解码接收到的数据
    # print('buffer=', buffer)
    data = buffer.decode('gbk')
    # print('Received data:', data)
    return data

  def GetNote(self):
    """获取纯文本笔记"""
    return self.SendData2('GN')

  def GetNodeMap(self) -> NodeMap:
    """获取NodeMap类笔记"""
    nodeMap = NodeMap()
    return nodeMap.LoadNote(self.SendData2('GN'))

  def ClearNote(self):
    """清空笔记内容"""
    self.SendData('CA')

  def AddNode(self, x, y, title, content='', color=(255, 128, 128), size=200, selected=False):
    """添加节点"""
    m = f'nbn {x} {y} {self.FormatText(title)} {self.FormatText(content)} {self.VBColor(color)} {size} {int(selected)}'
    self.SendData(m)

  def EditNode(self, nid, title, content='', color=(255, 128, 128), size=200):
    """编辑节点"""
    m = f'en {nid} {self.FormatText(title)} {self.FormatText(content)} {self.VBColor(color)} {size}'
    self.SendData(m)

  def AddLink(self, sid, tid, content='', width=3, selected=False):
    """添加链接"""
    m = f'nbl {sid} {tid} {self.FormatText(content)} {width} {int(selected)}'
    self.SendData(m)

  def EditLink(self, sid, tid, content='', width=3):
    """编辑链接"""
    m = f'el {sid} {tid} {self.FormatText(content)} {width}'
    self.SendData(m)

  def Destroy(self):
    """释放API连接"""
    self.s.close()


if __name__ == '__main__':
  note2dApi = Note2dApi()
  note2dApi.AddNode(500, 500, 'hello world', '这是一个测试内容', (255, 0, 0))
  time.sleep(3)
  note2dApi.EditNode(0, '新标题', '新内容', (255, 255, 0))
  note2dApi.AddNode(500, 5000, 'hello world', '这是一个测试内容', (255, 0, 0))
  note2dApi.AddLink(0, 1, '0 连接 1')
  time.sleep(1)
  note2dApi.EditLink(0, 1, '无效连接')
  note2dApi.Destroy()
